﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using DiscoveryLogic.Common.Utilities;
using DiscoveryLogic.DataViz.NetworkDiagram.NetworkData;

namespace DiscoveryLogic.DataViz.NetworkDiagram.LinkerControls
{
    /// <summary>
    /// 
    /// </summary>
    public class NetworkLinker : INetworkJoinCalcuator
    {
        #region Implementation of INetworkJoinCalcuator

        /// <summary>
        /// only left join is implemented
        /// </summary>
        public void Join(Network net1, string propName1, Network net2, string propName2, 
            NetworkLinkBehavior behavior, ref List<Vertice> vertices, ref List<Edge> edges)
        {
            if(net1.NetData.FieldDataTypes[propName1]!=net2.NetData.FieldDataTypes[propName2])
                throw new Exception("Type do not match for join");

            DataTable dt1 = new DataTable();
            List<LinkDataObject> links1=new List<LinkDataObject>();
            net1.PopulateNetworkData(ref dt1, ref links1);
            // prop1 value -> list of keys
            Dictionary<object, List<string>> prop1ValKeyMapping = new Dictionary<object, List<string>>();
            if (dt1 == null || dt1.Rows.Count == 0)
            {
                throw new Exception("Unable to read data");
            }
            foreach (DataRow dr1 in dt1.Rows)
            {
                if(dr1[propName1]==null || dr1[propName1]==DBNull.Value)
                    continue;
                string pkValue = dr1[net1.NetData.PkFieldName].ToString();
                object prop1Value = dr1[propName1];
                if (prop1ValKeyMapping.ContainsKey(prop1Value))
                {
                    List<string> keys = prop1ValKeyMapping[prop1Value];
                    keys.Add(pkValue);
                    prop1ValKeyMapping[prop1Value] = keys;
                }
                else
                {
                    List<string> keys = new List<string>();
                    keys.Add(pkValue);
                    prop1ValKeyMapping.Add(prop1Value, keys);
                }
            }
            // NetDO key -> list of vertice IDs
            Dictionary<string, List<Guid>> netDOVerticeMappings = new Dictionary<string, List<Guid>>();
            foreach (Vertice v in vertices)
            {
                if (v.NetworkName == net1.NetworkName)
                {
                    string key = v.PkValue;
                    if (netDOVerticeMappings.ContainsKey(key))
                    {
                        List<Guid> vIDs = netDOVerticeMappings[key];
                        vIDs.Add(v.ID);
                        netDOVerticeMappings[key] = vIDs;
                    }
                    else
                    {
                        List<Guid> vIDs = new List<Guid>();
                        vIDs.Add(v.ID);
                        netDOVerticeMappings.Add(key, vIDs);
                    }
                }
            }

            NetworkDataObject netDO2 = net2.NetData;
            Dictionary<string, Vertice> pk2ValsAdded=new Dictionary<string, Vertice>();
            DataTable dt2 = new DataTable();
            List<LinkDataObject> links2 = new List<LinkDataObject>();
            net2.PopulateNetworkData(ref dt2, ref links2);
            if (dt2 == null || dt2.Rows.Count == 0)
                throw new Exception("Unable to read data");
            foreach(DataRow dr2 in dt2.Rows)
            {
                if(dr2[propName2]==null || dr2[propName2]==DBNull.Value)
                    continue;
                string pkValue2 = dr2[net2.NetData.PkFieldName].ToString();
                string label2 = pkValue2;
                if(!string.IsNullOrEmpty(net2.NetData.LabelFieldName) && dr2[net2.NetData.LabelFieldName]!=null 
                    && dr2[net2.NetData.LabelFieldName]!=DBNull.Value)
                {
                    label2 = dr2[net2.NetData.LabelFieldName].ToString();
                }
                object prop2Value = dr2[propName2];
                if(prop1ValKeyMapping.ContainsKey(prop2Value))
                {
                    Vertice v2 = new Vertice(Guid.NewGuid(), label2, new PointF(), net2.NetworkName, pkValue2);
                    vertices.Add(v2);
                    pk2ValsAdded.Add(pkValue2, v2);
                    List<string> srcKeys = prop1ValKeyMapping[prop2Value];
                    List<Guid> vIDsToJoin=new List<Guid>();
                    foreach(string key in srcKeys)
                    {
                        if(netDOVerticeMappings.ContainsKey(key))
                        {
                            List<Guid> vIDs = netDOVerticeMappings[key];
                            foreach(Guid vID in vIDs)
                            {
                                if(!vIDsToJoin.Contains(vID))
                                    vIDsToJoin.Add(vID);
                            }
                        }
                    }
                    if(vIDsToJoin.Count>0)
                    {
                        foreach(Guid vID in vIDsToJoin)
                        {
                            LinkDataObject link = new LinkDataObject(
                                net1.NetworkName, net2.NetworkName, 
                                propName1, propName2,
                                StringUtil.ToString(srcKeys.ToArray()),
                                pkValue2, 1);
                            if (netDO2.IsLinkDirectional)
                            {
                                Arc e = new Arc(Guid.NewGuid(), "", (float)link.Distance, vID, v2.ID, link);
                                edges.Add(e);
                            }
                            else
                            {
                                Edge e = new Edge(Guid.NewGuid(), "", (float) link.Distance, vID, v2.ID, link);
                                edges.Add(e);
                            }
                        }
                    }
                }
            }

            if(pk2ValsAdded.Count>0 && links2 !=null && links2.Count>0)
            {
                foreach(LinkDataObject link2 in links2)
                {
                    if (pk2ValsAdded.ContainsKey(link2.FromKey) && pk2ValsAdded.ContainsKey(link2.ToKey))
                    {
                        Vertice fromV = pk2ValsAdded[link2.FromKey];
                        Vertice toV = pk2ValsAdded[link2.ToKey];
                        if (netDO2.IsLinkDirectional)
                        {
                            Arc e = new Arc(Guid.NewGuid(), "", (float)link2.Distance, fromV.ID, toV.ID);
                            edges.Add(e);
                        }
                        else
                        {
                            Edge e = new Edge(Guid.NewGuid(), "", (float) link2.Distance, fromV.ID, toV.ID);
                            edges.Add(e);
                        }
                    }
                }
            }
        }

        #endregion

        #region outer join 

        #endregion 

        #region left inner join 

        #endregion 

        #region right inner join 

        #endregion
    }
}
